{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a734a988-5048-4d34-85cd-1728edd8b3ed",
   "metadata": {},
   "source": [
    "# 1 Build a Basic Chatbot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2a0c7cc9-2325-404c-abff-75312162c309",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from langchain_ollama import ChatOllama\n",
    "llm = ChatOllama(base_url=\"http://192.168.99.142:11434\", model=\"hhao/qwen2.5-coder-tools:latest\")\n",
    "\n",
    "from typing import Annotated\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Messages have the type \"list\". The `add_messages` function\n",
    "    # in the annotation defines how this state key should be updated\n",
    "    # (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "# The first argument is the unique node name\n",
    "# The second argument is the function or object that will be called whenever\n",
    "# the node is used.\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "graph_builder.add_edge(\"chatbot\", END)\n",
    "graph = graph_builder.compile()\n",
    "\n",
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "dd1e9af2-7528-435f-818d-eb0d88e4c551",
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "User:  what is 2+19?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Assistant: ```json\n",
      "{\n",
      "  \"name\": \"modify_code\",\n",
      "  \"arguments\": {\n",
      "    \"code\": \"2 + 19\"\n",
      "  }\n",
      "}\n",
      "```\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "User:  hi  i am hawk.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Assistant: Hello Hawk! How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "User:  what is my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Assistant: I don't have access to personal information such as your name unless it has been shared with me in the course of our conversation. If you need assistance with a specific coding task or tool use, please provide more details so I can help you effectively.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "User:  q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Goodbye!\n"
     ]
    }
   ],
   "source": [
    "def stream_graph_updates(user_input: str):\n",
    "    for event in graph.stream({\"messages\": [{\"role\": \"user\", \"content\": user_input}]}):\n",
    "        for value in event.values():\n",
    "            print(\"Assistant:\", value[\"messages\"][-1].content)\n",
    "\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        user_input = input(\"User: \")\n",
    "        if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "            print(\"Goodbye!\")\n",
    "            break\n",
    "        stream_graph_updates(user_input)\n",
    "        \n",
    "    except:\n",
    "        # fallback if input() is not available\n",
    "        user_input = \"What do you know about LangGraph?\"\n",
    "        print(\"User: \" + user_input)\n",
    "        stream_graph_updates(user_input)\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0524ce36-ab42-4962-be07-adc9723a164a",
   "metadata": {},
   "source": [
    "## 2 Enhancing the Chatbot with Tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "6efaf27c-79e4-499d-8daf-dc20b650daf5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[StructuredTool(name='multiply', description='multi first_int and second_int', args_schema=<class 'langchain_core.utils.pydantic.multiply'>, func=<function multiply at 0x00000216836F0FE0>), StructuredTool(name='add', description='add first_int and second_int', args_schema=<class 'langchain_core.utils.pydantic.add'>, func=<function add at 0x00000216836F1080>)]\n"
     ]
    }
   ],
   "source": [
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "from langchain_core.tools import tool\n",
    "# 定义函数并应用工具装饰器\n",
    "@tool\n",
    "def multiply(first_int: int, second_int: int) -> int:\n",
    "    \"\"\"multi first_int and second_int\"\"\"\n",
    "    return first_int * second_int\n",
    "\n",
    "@tool\n",
    "def add(first_int: int, second_int: int) -> int:\n",
    "    \"\"\"add first_int and second_int\"\"\"\n",
    "    return first_int + second_int\n",
    "\n",
    "tools = [multiply, add]\n",
    "print(tools)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "4f8de9e3-2f07-431b-a975-650a70b460d6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<langgraph.graph.state.StateGraph at 0x21683683350>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from typing import Annotated\n",
    "from langchain_ollama import ChatOllama\n",
    "llm = ChatOllama(base_url=\"http://192.168.99.142:11434\", model=\"hhao/qwen2.5-coder-tools:latest\")\n",
    "\n",
    "from langchain_core.messages import BaseMessage\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "tool_node = ToolNode(tools=[tool])\n",
    "graph_builder.add_node(\"tools\", tool_node)\n",
    "\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"chatbot\",\n",
    "    tools_condition,\n",
    ")\n",
    "# Any time a tool is called, we return to the chatbot to decide the next step\n",
    "graph_builder.add_edge(\"tools\", \"chatbot\")\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "\n",
    "from IPython.display import Image, display\n",
    "try:\n",
    "    display(Image(graph_builder.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
